use crate::common::quic::QuicTls;
use crate::utils::validator::{base64_validator, cert_validator, uuid_validator};
use anyhow::{Error, Result};
use base64::Engine;
use base64::prelude::BASE64_STANDARD;
use quinn::Endpoint;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::sync::Arc;
use tokio::sync::RwLock;
use uuid::Uuid;
use validator::{Validate, ValidationError};

#[derive(Serialize, Deserialize, Validate, PartialEq, Debug)]
#[validate(schema(function = "TcpPortsYaml::custom_validate"))]
pub struct TcpPortsYaml {
    pub enable: bool,
    pub from: u16,
    pub to: u16,
}

impl TcpPortsYaml {
    pub fn custom_validate(v: &TcpPortsYaml) -> Result<(), ValidationError> {
        if v.enable {
            if v.from <= v.to {
                Ok(())
            } else {
                Err(ValidationError::new("from is more than to"))
            }
        } else {
            Ok(())
        }
    }
}

#[derive(Serialize, Deserialize, Validate, PartialEq, Debug)]
#[validate(schema(function = "ProxyConfigYaml::custom_validate"))]
pub struct ProxyConfigYaml {
    #[validate(custom(function = "base64_validator"))]
    pub ca_base64: String,

    #[validate(custom(function = "base64_validator"))]
    pub cert_base64: String,

    #[validate(custom(function = "base64_validator"))]
    pub key_base64: String,

    pub quic_port: u16,

    #[validate(custom(function = "uuid_validator"))]
    pub uuid: String,

    #[validate(nested)]
    pub tcp_port: Option<TcpPortsYaml>,
}

impl ProxyConfigYaml {
    /// 校验
    pub fn custom_validate(v: &ProxyConfigYaml) -> Result<(), ValidationError> {
        if v.quic_port == 0 || v.quic_port > u16::MAX {
            return Err(ValidationError::new("quic_port out of range"));
        }
        let ca_pem = if let Ok(c) = BASE64_STANDARD.decode(&v.ca_base64) {
            c
        } else {
            return Err(ValidationError::new("decoded ca_base64 error"));
        };
        let cert_pem = if let Ok(c) = BASE64_STANDARD.decode(&v.cert_base64) {
            c
        } else {
            return Err(ValidationError::new("decoded cert_base64 error"));
        };
        let key_pem = if let Ok(c) = BASE64_STANDARD.decode(&v.key_base64) {
            c
        } else {
            return Err(ValidationError::new("decoded key_base64 error"));
        };
        match cert_validator(&ca_pem, &cert_pem, &key_pem) {
            Ok(()) => Ok(()),
            Err(_) => Err(ValidationError::new("cert validator error")),
        }
    }

    /// 从文件中解析配置并校验
    pub fn parse_from_file(path: &str) -> Result<Self> {
        let content = std::fs::read_to_string(&path)?;
        let cfg: ProxyConfigYaml = serde_yaml::from_str(&content)?;
        cfg.validate()?;
        Ok(cfg)
    }
}

#[test]
fn test_proxy_config() {
    let cfg_str = include_str!("./config.yaml");
    let cfg: ProxyConfigYaml = serde_yaml::from_str(cfg_str).unwrap();
    cfg.validate().unwrap();
}

pub struct TcpPorts {
    pub from: u16,
    pub to: u16,
    used: BTreeSet<u16>,
}

impl TcpPorts {
    pub fn new(from: u16, to: u16) -> Self {
        TcpPorts {
            from,
            to,
            used: BTreeSet::new(),
        }
    }

    pub fn add(&mut self, port: u16) -> Result<()> {
        if port > self.to || port < self.from {
            return Err(Error::msg("port out of range"));
        }
        if self.used.contains(&port) {
            return Err(Error::msg("port already used"));
        }
        self.used.insert(port);
        Ok(())
    }

    pub fn remove(&mut self, port: u16) -> Result<()> {
        if port > self.to || port < self.from {
            return Err(Error::msg("port out of range"));
        }
        if !self.used.contains(&port) {
            return Err(Error::msg("port not used"));
        }
        self.used.remove(&port);
        Ok(())
    }

    pub fn remote_multi(&mut self, ports: &[u16]) -> Result<()> {
        for i in ports {
            self.used.remove(i);
        }
        Ok(())
    }
    
    /// 判断端口是否在范围内且没有使用
    pub fn can_add(&self, port: u16) -> bool {
        self.to >= port && self.from <= port && !self.used.contains(&port)
    }

    pub fn exists(&self, port: u16) -> bool {
        self.used.contains(&port)
    }
}

pub struct ProxyConfig {
    pub quic_port: u16,
    pub tls: QuicTls,
    pub uuid: Uuid,
    pub dev_mode: bool,
}

impl ProxyConfig {
    pub fn from_yaml(yaml: &ProxyConfigYaml) -> (Self, Option<Arc<RwLock<TcpPorts>>>) {
        let ca_pem = BASE64_STANDARD.decode(yaml.ca_base64.as_bytes()).unwrap();
        let cert_pem = BASE64_STANDARD.decode(yaml.cert_base64.as_bytes()).unwrap();
        let key_pem = BASE64_STANDARD.decode(yaml.key_base64.as_bytes()).unwrap();

        let tls = QuicTls::new(ca_pem.as_slice(), cert_pem.as_slice(), key_pem.as_slice());
        let uuid = Uuid::parse_str(yaml.uuid.as_str()).unwrap();
        let tcp_port = if let Some(t) = &yaml.tcp_port {
            if t.enable {
                Some(Arc::new(RwLock::new(TcpPorts::new(t.from, t.to))))
            } else {
                None
            }
        } else {
            None
        };
        (
            ProxyConfig {
                quic_port: yaml.quic_port,
                tls,
                uuid,
                dev_mode: false,
            },
            tcp_port,
        )
    }

    pub fn set_dev_mode(&mut self, dev_mode: bool) {
        self.dev_mode = dev_mode;
    }

    pub fn quic(&self) -> Result<Endpoint> {
        let server_cfg = self.tls.server();
        let e = Endpoint::server(
            server_cfg,
            std::net::SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, self.quic_port)),
        )?;
        Ok(e)
    }
}
